package com.lemon.rule.express.instruction;

import java.util.Stack;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.lemon.rule.express.CallResult;
import com.lemon.rule.express.ExpressLoader;
import com.lemon.rule.express.ExpressRunner;
import com.lemon.rule.express.IExpressContext;
import com.lemon.rule.express.InstructionSet;
import com.lemon.rule.express.InstructionSetContext;
import com.lemon.rule.express.OperateData;
import com.lemon.rule.express.RunEnvironment;
import com.lemon.rule.express.instruction.opdata.OperateDataArrayItem;
import com.lemon.rule.express.instruction.opdata.OperateDataAttr;
import com.lemon.rule.express.instruction.opdata.OperateDataField;
import com.lemon.rule.express.instruction.opdata.OperateDataKeyValue;
import com.lemon.rule.express.instruction.opdata.OperateDataLocalVar;

public class OperateDataCacheManager {

    private static final Logger logger = LoggerFactory.getLogger(OperateDataCacheManager.class);

    private static ThreadLocal<RunnerDataCache> m_OperateDataObjectCache = new ThreadLocal<RunnerDataCache>() {
        protected RunnerDataCache initialValue() {
            return new RunnerDataCache();
        }
    };

    public static void push(ExpressRunner aRunner) {
        logger.info(JSON.toJSONString(aRunner));
        m_OperateDataObjectCache.get().push(aRunner);
    }

    public static IOperateDataCache getOperateDataCache() {
        return m_OperateDataObjectCache.get().getOperateDataCache();
    }

    public static OperateData fetchOperateData(Object obj, Class<?> aType) {
        return getOperateDataCache().fetchOperateData(obj, aType);
    }

    public static OperateDataAttr fetchOperateDataAttr(String name, Class<?> aType) {
        return getOperateDataCache().fetchOperateDataAttr(name, aType);
    }

    public static OperateDataLocalVar fetchOperateDataLocalVar(String name, Class<?> aType) {
        return getOperateDataCache().fetchOperateDataLocalVar(name, aType);
    }

    public static OperateDataField fetchOperateDataField(Object aFieldObject, String aFieldName) {
        return getOperateDataCache().fetchOperateDataField(aFieldObject, aFieldName);
    }

    public static OperateDataArrayItem fetchOperateDataArrayItem(OperateData aArrayObject, int aIndex) {
        return getOperateDataCache().fetchOperateDataArrayItem(aArrayObject, aIndex);
    }

    public static OperateDataKeyValue fetchOperateDataKeyValue(OperateData aKey, OperateData aValue) {
        return getOperateDataCache().fetchOperateDataKeyValue(aKey, aValue);
    }

    public static RunEnvironment fetRunEnvironment(InstructionSet aInstructionSet, InstructionSetContext aContext, boolean aIsTrace) {
        return getOperateDataCache().fetRunEnvironment(aInstructionSet, aContext, aIsTrace);
    }

    public static CallResult fetchCallResult(Object aReturnValue, boolean aIsExit) {
        return getOperateDataCache().fetchCallResult(aReturnValue, aIsExit);
    }

    public static InstructionSetContext fetchInstructionSetContext(boolean aIsExpandToParent, ExpressRunner aRunner, IExpressContext<String, Object> aParent, ExpressLoader aExpressLoader, boolean aIsSupportDynamicFieldName) {
        return getOperateDataCache().fetchInstructionSetContext(aIsExpandToParent, aRunner, aParent, aExpressLoader, aIsSupportDynamicFieldName);
    }

    public static long getFetchCount() {
        return getOperateDataCache().getFetchCount();
    }

    public static void resetCache(ExpressRunner aRunner) {
        getOperateDataCache().resetCache();
        m_OperateDataObjectCache.get().pop(aRunner);

    }

}

class RunnerDataCache {
    IOperateDataCache cache;

    Stack<ExpressRunner> stack = new Stack<ExpressRunner>();

    public void push(ExpressRunner aRunner) {
        this.cache = aRunner.getOperateDataCache();
        this.stack.push(aRunner);
    }

    public IOperateDataCache getOperateDataCache() {
        return this.cache;
    }

    public void pop(ExpressRunner aRunner) {
        this.cache = this.stack.pop().getOperateDataCache();

    }

}